#include "chatservice.hpp"
#include "public.hpp"
#include <muduo/base/Logging.h>
#include <vector>
#include <string>
#include <iostream>
#include "redis.hpp"

using namespace std;

ChatService *ChatService::instance()
{
    static ChatService service;
    return &service;
}

//处理登陆请求
void ChatService::login(const TcpConnectionPtr &conn, json &js, Timestamp)
{
    int id = js["id"];
    string pwd = js["password"];

    User user = um_.query(id);
    if (user.getId() == id && user.getPassword() == pwd)
    {
        if (user.getState() == "online")
        {
            json response;
            response["msgid"] = LOGIN_MSG_ACK;
            response["errno"] = 2;
            response["errmsg"] = "user is online";
            conn->send(response.dump());
        }
        else
        {
            {
                lock_guard<mutex> lock(connMtx_);
                connMap_.insert({id, conn});
            }

            redis_.subscribe(id);

            user.setState("online");
            um_.updateState(user);

            json response;
            response["msgid"] = LOGIN_MSG_ACK;
            response["errno"] = 0;
            response["id"] = id;
            response["name"] = user.getName();
            //查询用户是否有离线消息
            vector<string> vec = offlineMsgModel_.query(id);
            if (!vec.empty())
            {
                response["offlinemsg"] = vec;
                //取出离线消息后清空数据库中相关数据
                offlineMsgModel_.remove(id);
            }
            //查询好友列表
            vector<User> friVec = friendModel_.query(id);
            if (!friVec.empty())
            {
                vector<string> vec2;
                for (User &user : friVec)
                {
                    json js;
                    js["id"] = user.getId();
                    js["name"] = user.getName();
                    js["state"] = user.getState();
                    vec2.push_back(js.dump());
                }
                response["friends"] = vec2;
            }

            //查询群组列表
            vector<Group> groupVec;
            groupVec = groupModel_.queryGroup(id);
            if (!groupVec.empty())
            {
                vector<string> groupStr;
                for (auto &group : groupVec)
                {
                    json groupjs;
                    groupjs["id"] = group.getGroupId();
                    groupjs["name"] = group.getGroupName();
                    groupjs["desc"] = group.getGroupDesc();
                    vector<string> groupUserVec;
                    for (auto groupUser : group.getGroupUsers())
                    {
                        json groupUserjs;
                        groupUserjs["id"] = groupUser.getId();
                        groupUserjs["name"] = groupUser.getName();
                        groupUserjs["state"] = groupUser.getState();
                        groupUserjs["role"] = groupUser.getRole();
                        groupUserVec.push_back(groupUserjs.dump());
                    }
                    groupjs["users"] = groupUserVec;
                    groupStr.push_back(groupjs.dump());
                }
                response["groups"] = groupStr;
            }

            conn->send(response.dump());
        }
    }
    else
    {
        json response;
        response["msgid"] = LOGIN_MSG_ACK;
        response["errno"] = 1;
        response["errmsg"] = "user not found";
        conn->send(response.dump());
    }
}

//处理注册请求
void ChatService::reg(const TcpConnectionPtr &conn, json &js, Timestamp)
{
    string name = js["name"];
    string pwd = js["password"];
    User user;
    user.setName(name);
    user.setPassword(pwd);

    bool state = um_.insert(user);
    if (state)
    {
        json response;
        response["msgid"] = REG_MSG_ACK;
        response["id"] = user.getId();
        response["errno"] = 0;
        conn->send(response.dump());
    }
    else
    {
        json response;
        response["msgid"] = REG_MSG_ACK;
        response["errno"] = 1;
        conn->send(response.dump());
    }
}

//初始化msgHandlerMap_，让请求类型和任务对应
ChatService::ChatService()
{
    msgHandlerMap_.insert({LOGIN_MSG, std::bind(&ChatService::login, this, placeholders::_1, placeholders::_2, placeholders::_3)});
    msgHandlerMap_.insert({REG_MSG, std::bind(&ChatService::reg, this, placeholders::_1, placeholders::_2, placeholders::_3)});
    msgHandlerMap_.insert({ONE_CHAT_MSG, std::bind(&ChatService::oneChat, this, placeholders::_1, placeholders::_2, placeholders::_3)});
    msgHandlerMap_.insert({ADD_FRIEND, std::bind(&ChatService::addFriend, this, placeholders::_1, placeholders::_2, placeholders::_3)});
    msgHandlerMap_.insert({CREATE_GROUP, std::bind(&ChatService::createGroup, this, placeholders::_1, placeholders::_2, placeholders::_3)});
    msgHandlerMap_.insert({ADD_GROUP, std::bind(&ChatService::addGroup, this, placeholders::_1, placeholders::_2, placeholders::_3)});
    msgHandlerMap_.insert({GROUP_CHAT, std::bind(&ChatService::chatGroup, this, placeholders::_1, placeholders::_2, placeholders::_3)});
    msgHandlerMap_.insert({EXIT_MSG, std::bind(&ChatService::exitProcess, this, placeholders::_1, placeholders::_2, placeholders::_3)});

    if (redis_.connect())
    {
        redis_.init_notify_handler(std::bind(&ChatService::handleRedisSubscribeMessage, this, placeholders::_1, placeholders::_2));
    }
}

//获取消息所对应的任务
MsgHandler ChatService::getHandler(int msgid)
{
    auto it = msgHandlerMap_.find(msgid);
    if (it == msgHandlerMap_.end())
    {
        return [=](const TcpConnectionPtr &conn, json &js, Timestamp)
        {
            LOG_ERROR << "msgid:" << msgid << "can not find!";
        };
    }
    else
    {
        return msgHandlerMap_[msgid];
    }
}

void ChatService::connCloseException(const TcpConnectionPtr &conn)
{
    User user;
    {
        lock_guard<mutex> lock(connMtx_);
        for (auto it = connMap_.begin(); it != connMap_.end(); ++it)
        {
            if (it->second == conn)
            {
                user.setId(it->first);
                connMap_.erase(it);
                break;
            }
        }
    }
    redis_.unsubscribe(user.getId());
    if (user.getId() != -1)
    {
        user.setState("offline");
        um_.updateState(user);
    }
}

void ChatService::oneChat(const TcpConnectionPtr &conn, json &js, Timestamp)
{
    int id = js["to"].get<int>();
    {
        lock_guard<mutex> lock(connMtx_);
        auto it = connMap_.find(id);
        if (it != connMap_.end())
        {
            //用户在线，转发消息
            it->second->send(js.dump());
            return;
        }
    }
    //用户不在线，可能在其他服务器上，查询数据库中用户的状态
    User user = um_.query(id);
    if (user.getState() == "online")
    {
        redis_.publish(user.getId(), js.dump());
        return;
    }
    //转发离线消息
    offlineMsgModel_.insert(id, js.dump());
}

void ChatService::reset()
{
    um_.resetState();
}

void ChatService::addFriend(const TcpConnectionPtr &conn, json &js, Timestamp)
{
    int id = js["id"].get<int>();
    int friendid = js["friendid"].get<int>();

    friendModel_.insert(id, friendid);
}

void ChatService::createGroup(const TcpConnectionPtr &conn, json &js, Timestamp)
{
    int id = js["id"].get<int>();
    string groupName = js["groupName"];
    string groupDesc = js["groupDesc"];

    Group group;
    group.setGroupName(groupName);
    group.setgroupDesc(groupDesc);
    if (groupModel_.createGroup(group))
    {
        groupModel_.addGroup(id, group.getGroupId(), "creator");
    }
}
void ChatService::addGroup(const TcpConnectionPtr &conn, json &js, Timestamp)
{
    int id = js["id"].get<int>();
    int groupid = js["groupid"].get<int>();
    groupModel_.addGroup(id, groupid, "normal");
}
void ChatService::chatGroup(const TcpConnectionPtr &conn, json &js, Timestamp)
{
    int id = js["id"].get<int>();
    int groupid = js["groupid"].get<int>();

    vector<int> users = groupModel_.queryGroupUeser(id, groupid);
    lock_guard<mutex> lock(connMtx_);
    for (auto toid : users)
    {
        auto it = connMap_.find(toid);
        if (it != connMap_.end())
        {
            it->second->send(js.dump());
        }
        else
        {
            User user = um_.query(toid);
            if (user.getState() == "online")
            {
                redis_.publish(user.getId(), js.dump());
            }
            else
            {
                offlineMsgModel_.insert(toid, js.dump());
            }
        }
    }
}

void ChatService::exitProcess(const TcpConnectionPtr &conn, json &js, Timestamp)
{
    int userid = js["id"];
    {
        lock_guard<mutex> lock(connMtx_);
        auto it = connMap_.find(userid);
        if (it != connMap_.end())
        {
            connMap_.erase(it);
        }
    }
    redis_.unsubscribe(userid);
    User user(userid, "", "", "offline");
    um_.updateState(user);
}

void ChatService::handleRedisSubscribeMessage(int userid, string msg)
{
    lock_guard<mutex> lock(connMtx_);
    auto it = connMap_.find(userid);
    if (it != connMap_.end())
    {
        it->second->send(msg);
        return;
    }

    offlineMsgModel_.insert(userid, msg);
}